package com.platform.module.user.aspect;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.platform.module.common.base.*;
import com.platform.module.common.constant.CacheConstant;
import com.platform.module.common.enums.app.AgentEnum;
import com.platform.module.common.enums.app.AppNameEnum;
import com.platform.module.common.redis.CacheClient;
import com.platform.module.common.utils.IPUtils;
import com.platform.module.common.utils.MD5Helper;
import com.platform.module.common.utils.TokenGenerator;
import com.platform.module.user.service.UserTokenService;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;


@Component
@Aspect
@Order(5)
public class CheckLoginAspect {

    public static final String MD5_COOKIEKEY = "ZqaCIx6zR2pRe";

    private static final Logger log = LoggerFactory.getLogger(CheckLoginAspect.class);
    @Value("${spring.application.name}")
    private String module;
    @Value("${token.key}")
    private String tokenKey;
    private final CacheClient cacheClient;
    private final HttpServletRequest request;
    private final UserTokenService userTokenService;
    public static ThreadLocal<Long> userId_TL = new ThreadLocal<>();
    public static ThreadLocal<Long> time_TL = new ThreadLocal<>();
    public static ThreadLocal<App> app_TL = new ThreadLocal<>();
    public static ThreadLocal<String> traceId_TL = new ThreadLocal<>();

    public CheckLoginAspect(CacheClient cacheClient, HttpServletRequest request, UserTokenService userTokenService) {
        this.cacheClient = cacheClient;
        this.request = request;
        this.userTokenService = userTokenService;
    }

    @Pointcut("execution(* com.platform.module.user.controller.*.*(..))")
    private void point() {
    }

    @Around("point()")
    public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
        time_TL.set(System.currentTimeMillis());
        traceId_TL.set(UUID.randomUUID().toString().replace("-", ""));

        String accessToken = this.getHeaderValue("Authorization");
        String signMD5 = this.getHeaderValue("sign");
        String curTimeStr = this.getHeaderValue("currentTime");
        String source = this.getHeaderValue("siteVersion");
        String agent = this.getHeaderValue("agent");
        String appName = this.getHeaderValue("siteName");

        App app = this.getApp();
        Long userId = null;
        String appId = null;
        String ip = app.getIp();
        String ipv6 = app.getIpv6();

        String agentId;
        try {
            Object[] objs = pjp.getArgs();

            for (Object obj : objs) {
                if (obj instanceof BaseReq domain) {
                    if (StringUtils.isNotBlank(accessToken)) {
                        String[] tt = TokenGenerator.authToken(accessToken, "appId", tokenKey);
                        userId = StringUtils.isEmpty(tt[2]) ? null : Long.valueOf(tt[2]);
                        if (userId != null) {
                            domain.setUserId(userId);
                        } else {
                            log.error("userId={} accessToken 非法 {}", null, accessToken);
                        }
                    }
                    domain.setApp(app);
                    domain.setIp(ip);
                    domain.setIpv6(ipv6);
                    AppNameEnum appNameEnum = AppNameEnum.valueOfAppName(appName);
                    if (appNameEnum != null && StringUtils.isNotBlank(appNameEnum.getAppId())) {
                        domain.setAppId(appNameEnum.getAppId());
                        appId = appNameEnum.getAppId();
                    }

                    if (AgentEnum.isGoogleApk(app.getSource())) {
                        domain.setIc(null);
                    }

                    if (StringUtils.isNotBlank(domain.getIc()) && "null".equals(domain.getIc())) {
                        domain.setIc(null);
                    }

                    if (StringUtils.isNotBlank(domain.getIc())) {
                        domain.setIc(domain.getIc().trim());
                    }

                    if (!StringUtils.isNumeric(domain.getIc())) {
                        domain.setIc(null);
                    }

                    if (StringUtils.isNotBlank(app.getAgent())) {
                        domain.setAgentParam(app.getAgent());
                    }

                    // 因为匹配到一个写死的，所以导致
                    if (AgentEnum.isInviteApk(app.getSource())) {
                        app.setAgent(AgentEnum.getAgentEnumByChannel(app.getSource()).getAgent());  // 这里导致 agent 变成按渠道查找的结果
                        domain.setAgentParam(AgentEnum.getAgentEnumByChannel(app.getSource()).getAgent());
                    }

                    if (StringUtils.isNotBlank(app.getAppsFlyerUID())) {
                        agentId = this.cacheClient.getStr("USER:APPSFLYERID:" + app.getAppsFlyerUID());
                        if (StringUtils.isNotBlank(agentId)) {
                            app.setAgent(agentId);
                            domain.setAgentParam(agentId);
                            log.info("agentId={}", agentId);
                        }
                    }

                    domain.setAccessToken(accessToken);
                }
            }

            userId_TL.set(userId);
            this.printReqLog(app, userId, accessToken);
        } catch (Exception var19) {
            log.error("AOP methodBefore:", var19);
        }

        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        NoNeedLogin noNeedLogin = method.getAnnotation(NoNeedLogin.class);
        NoNeedCheckHeader noNeedCheckHeader = method.getAnnotation(NoNeedCheckHeader.class);
        if (noNeedCheckHeader == null) {
            boolean b = this.checkHeader(app);
            if (!b) {
                return CommonResp.fail(CodeDict.请求头错误.getCode(), CodeDict.请求头错误.getMsg());
            }
        }

        if (noNeedLogin == null) {
            if (userId == null) {
                log.error("非法请求 userId={} curTime={} signMD5={} source={} agent={} ip={} ipv6={} URL={} accessToken={}",
                        null, curTimeStr, signMD5, source, agent, ip, ipv6, this.request.getRequestURL(), accessToken);
                return CommonResp.fail(CodeDict.未登录.getCode(), CodeDict.未登录.getMsg());
            }

            String cacheToken = this.cacheClient.getStr(CacheConstant.USER_LOGIN_TOKEN + userId);
            if (StringUtils.isEmpty(cacheToken) || !accessToken.equals(cacheToken)) {
                log.warn("过期的accessToken userId={} curTime={} signMD5={} source={} agent={} ip={} ipv6={} URL={} accessToken={}",
                        userId, curTimeStr, signMD5, source, agent, ip, ipv6, this.request.getRequestURL(), accessToken);
                if (!StringUtils.isNotBlank(appId)) {
                    return CommonResp.fail(CodeDict.未登录.getCode(), CodeDict.未登录.getMsg());
                }

                this.userTokenService.setUserToken(userId, accessToken);
                log.warn("续约的accessToken userId={} curTime={} signMD5={} source={} agent={} ip={} ipv6={} URL={} accessToken={}",
                        userId, curTimeStr, signMD5, source, agent, ip, ipv6, this.request.getRequestURL(), accessToken);
                return pjp.proceed();
            }

            agentId = this.cacheClient.getStr(CacheConstant.USER_LOGIN_COOKIEKEY + userId);
            if (StringUtils.isBlank(signMD5)
                    || signMD5.length() != 32
                    || StringUtils.isBlank(curTimeStr)
                    || !StringUtils.isNumeric(curTimeStr)
                    || StringUtils.isBlank(agentId)
                    || agentId.length() < 32) {
                log.error("非法请求 userId={} ckky={} curTime={} signMD5={} source={} agent={} ip={} ipv6={} URL={} accessToken={}",
                        userId, agentId, curTimeStr, signMD5, source, agent, ip, ipv6, this.request.getRequestURL(), accessToken);
                return CommonResp.fail(CodeDict.未登录.getCode(), CodeDict.未登录.getMsg());
            }

            String md5Server = MD5Helper.md5Hex(cacheToken + agentId + curTimeStr, MD5_COOKIEKEY);
            if (StringUtils.isBlank(signMD5) || StringUtils.isBlank(md5Server) || !signMD5.equals(md5Server)) {
                log.error("非法请求 userId={} ckky={} curTime={} signMD5={} md5Server={} source={} agent={} ip={} ipv6={} URL={} accessToken={}",
                        userId, agentId, curTimeStr, signMD5, md5Server, source, agent, ip, ipv6, this.request.getRequestURL(), accessToken);
                if (userId != 100453L) {
                    return CommonResp.fail(CodeDict.未登录.getCode(), CodeDict.未登录.getMsg());
                }
            }

            if (StringUtils.isBlank(source) || "null".equals(source)) {
                log.error("非法请求 userId={} curTime={} signMD5={} source={} agent={} ip={} ipv6={} URL={} accessToken={}",
                        userId, curTimeStr, signMD5, source, agent, ip, ipv6, this.request.getRequestURL(), accessToken);
            }
        }

        return pjp.proceed();
    }

    private App getApp() {
        String referer = this.getHeaderValue("referer");
        String appName = this.getHeaderValue("siteName");
        String appVersion = this.getHeaderValue("h5Version");
        String appHotVersion = this.getHeaderValue("h5VersionA");
        String signMD5 = this.getHeaderValue("sign");
        String curTime = this.getHeaderValue("currentTime");
        String appMuId = this.getHeaderValue("appMuId");
        String phoneModel = this.getHeaderValue("phoneModel");
        String imei = this.getHeaderValue("hkey");
        String oaid = this.getHeaderValue("oaid");
        String androidId = this.getHeaderValue("androidId");
        String appsFlyerUID = this.getHeaderValue("appsFlyerUID");
        String areaCode = this.getHeaderValue("areaCode");
        String source = this.getHeaderValue("siteVersion");
        String agent = this.getHeaderValue("agent");
        String inviteCode = this.getHeaderValue("inviteCode");
        String ip = IPUtils.getIpFromRequest(this.request);
        String ipv6 = IPUtils.getIpV6FromRequest(this.request);
        String emulator = this.getHeaderValue("emulator");
        boolean isRooted = false;
        if (this.getHeaderValue("isSuper") != null && StringUtils.isNotBlank(this.getHeaderValue("isSuper"))) {
            try {
                isRooted = Boolean.parseBoolean(this.getHeaderValue("isSuper"));
            } catch (Exception var31) {
                log.error("异常信息", var31);
            }
        }

        String uuid = this.getHeaderValue("uuid");
        String deviceId = this.getHeaderValue("agentDetail");
        String params = this.getHeaderValue("params");
        String gpParams = this.getHeaderValue("gpParams");
        String deviceJson = this.getHeaderValue("agentJson");
        String campaign;
        if (StringUtils.isNotBlank(params) && JSON.isValid(params)) {
            try {
                JSONObject jsonObject = JSONObject.parseObject(params);
                if (jsonObject != null) {
                    campaign = jsonObject.getString("campaign");
                    if (StringUtils.isNotBlank(campaign) && campaign.contains("_") && campaign.split("_").length >= 1) {
                        agent = campaign.split("_")[1];
                    }
                }
            } catch (Exception var30) {
                log.error("异常信息", var30);
            }
        }

        String newImei = this.getHeaderValue("newImei");
        campaign = this.getHeaderValue("newImsi");
        String newMac = this.getHeaderValue("newMac");
        String newGps = this.getHeaderValue("newGps");
        return App.parseSafe(appName, appVersion, appHotVersion, signMD5, curTime,
                appMuId, phoneModel, areaCode, source, agent, inviteCode, ip, ipv6,
                imei, oaid, androidId, appsFlyerUID, emulator, isRooted, uuid, deviceId,
                params, gpParams, deviceJson, referer, newImei, campaign, newMac, newGps);
    }

    protected String getHeaderValue(String key) {
        return this.request.getHeader(key) == null ? null : this.request.getHeader(key);
    }

    private boolean checkHeader(App app) {
        boolean b = false;
        if (app == null) {
            return false;
        }
        if (app.getAppName() == null) {
            return false;
        }
        String appName = app.getAppName().getName();
        AppNameEnum appNameEnum = AppNameEnum.valueOfAppName(appName);
        if (appNameEnum != null) {
            b = true;
        } else {
            log.error("appName={} app={}", appName, app);
        }
        return b;
    }

    private void printReqLog(App app, Long userId, String accessToken) {
        StringBuilder param = new StringBuilder();
        Map<String, String[]> parameterMap = this.request.getParameterMap();
        parameterMap.forEach((k, v) -> {
            if (StringUtils.isBlank(param.toString())) {
                param.append(" ");
            }

            param.append(k).append(":").append(Arrays.toString(v)).append(" ");
        });
        log.info("userId:{},traceId:{},请求接口:{},app:{},参数:{} accessToken:{}",
                userId, traceId_TL.get(), this.request.getRequestURL(), app, param, accessToken);
    }

    @AfterReturning(
            returning = "rvt",
            pointcut = "point()"
    )
    public void methodAfterLog(JoinPoint joinPoint, Object rvt) {
        String traceId = this.getHeaderValue("traceId");
        String ret = "";

        try {
            if (rvt == null) {
                log.error("userId:{},traceId:{},返回数据:{}", userId_TL.get(), traceId_TL.get(), ret);
            } else {
                try {
                    JSONObject json = (JSONObject) JSON.toJSON(rvt);
                    if (json != null) {
                        if (json.get("data") != null && json.get("data").toString().length() > 1000) {
                            json.put("data", "simplified log output...");
                        }

                        ret = json.toJSONString();
                    }
                } catch (Exception var6) {
                    ret = String.valueOf(rvt);
                }

                log.info("cost={}ms,userId:{},traceId:{},接口响应:{},返回数据:{}", System.currentTimeMillis() - time_TL.get(), userId_TL.get(), traceId_TL.get(), this.request.getRequestURL(), ret);
            }
        } catch (Exception var7) {
            log.error("异常信息", var7);
            log.error("traceId:{},日志打印出错 返回数据:{}", traceId, ret);
        }

        userId_TL.remove();
        time_TL.remove();
        app_TL.remove();
        traceId_TL.remove();
    }
}
